package com.ar4j.bench.hibernate.service;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Service;

import com.ar4j.bench.common.domain.CategoryWithProductsWithItems;
import com.ar4j.bench.common.domain.ICategory;
import com.ar4j.bench.common.domain.IItem;
import com.ar4j.bench.common.domain.ProductWithItems;
import com.ar4j.bench.common.service.ICategoryService;
import com.ar4j.bench.hibernate.domain.Category;
import com.ar4j.bench.hibernate.domain.Item;
import com.ar4j.bench.hibernate.domain.Product;

/**
 * A category service implemented using the Hibernate library
 */
@Service
public class CategoryHibernateService extends HibernateDaoSupport implements ICategoryService {

  @Autowired
  public void setAutowiredSessionFactory(SessionFactory factory) {
    super.setSessionFactory(factory);
  }
  
  @Override
  public Long addCategory(ICategory category) {
    getHibernateTemplate().save(category);
    return category.getId();
  }

  @Override
  public Long addCategoryWithProductsWithItems(CategoryWithProductsWithItems category) {
    Category cat = (Category) category.getCategory();
    Set<Product> productSet = new HashSet<Product>();
    for(ProductWithItems withItems : category.getProducts()) {
      Product product = (Product) withItems.getProduct();
      Set<Item> itemSet = new HashSet<Item>();
      for(IItem item : withItems.getItems()) {
        Item cur = (Item) item;
        cur.setProduct(product);
        itemSet.add(cur);
      }
      
      product.setCategory(cat);
      product.setItems(itemSet);
      productSet.add(product);
    }
    
    cat.setProducts(productSet);
    getHibernateTemplate().save(cat);
    
    return cat.getId();
  }

  @Override
  public ICategory findById(Long id) {
    return getHibernateTemplate().get(Category.class, id);
  }

  @Override
  public CategoryWithProductsWithItems findCategoryWithProductsWithItemsById(final Long id) {
    return getHibernateTemplate().execute(new HibernateCallback<CategoryWithProductsWithItems>() {

      @Override
      public CategoryWithProductsWithItems doInHibernate(Session session) throws HibernateException, SQLException {
        Category category = (Category) session.get(Category.class, id);
        
        CategoryWithProductsWithItems out = new CategoryWithProductsWithItems();
        out.setCategory(category);
        
        List<ProductWithItems> withItemsList = new ArrayList<ProductWithItems>();
        for(Product product : category.getProducts()) {
          ProductWithItems withItems = new ProductWithItems();
          withItems.setProduct(product);
          withItems.setItems(new ArrayList<IItem>(product.getItems()));
          withItemsList.add(withItems);
        }
        
        out.setProducts(withItemsList);
        
        return out;
      }
    });
    
  }

  @Override
  public void removeById(Long id) {
    getHibernateTemplate().delete(findById(id));
  }

  @Override
  public void removeCategoryWithProductsWithItemsById(Long id) {
    removeById(id);
  }

  @Override
  public void updateCategory(ICategory category) {
    getHibernateTemplate().update(category);
  }

  @Override
  public void updateCategoryWithProductsWithItems(final CategoryWithProductsWithItems category) {
    getHibernateTemplate().execute(new HibernateCallback<Object>() {

      @Override
      public CategoryWithProductsWithItems doInHibernate(Session session) throws HibernateException, SQLException {
        Category cat = (Category) category.getCategory();
        Set<Product> productSet = new HashSet<Product>();
        for(ProductWithItems withItems : category.getProducts()) {
          Product product = (Product) withItems.getProduct();
          Set<Item> itemSet = new HashSet<Item>();
          for(IItem item : withItems.getItems()) {
            Item cur = (Item) item;
            cur.setProduct(product);
            itemSet.add(cur);
          }
          
          product.setCategory(cat);
          product.setItems(itemSet);
          productSet.add(product);
        }
        
        cat.setProducts(productSet);
        session.update(cat);
        
        return null;
      }
    });
  }

}
